<?php 
/**
 * This file is part of DomMvc.
 * 
 * DomMvc is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * DomMvc is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with DomMvc.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * @link http://inditel.ee/
 * @copyright 2007-2009 Inditel Meedia OÜ
 * @author Oliver Leisalu <oliver@inditel.ee>
 */

/**
 * This library is not free software. You can not redistribute it and/or modify it in anyway!
 * 
* @package DomMvc
* Extended smarty with functionality for Controller logic.
*/
class DomView extends DomSmarty implements DomMonitorable {

	
	/**
	* @param String
	*/
	private $template;
	
	/**
	* @param String
	*/
	private $cacheId;
		
	/**
	* @param Array
	*/
	private $partitioners;
	
	/**
	* @param String $template
	* @param Int $cacheTime
	* @param String $cacheId
	*/
	public function __construct( $template = NULL, $cacheTime = 0, $cacheId = NULL  ) {
		
		$this->template_dir = DOM_TEMPLATES_DIR;
		$this->compile_dir = SMARTY_COMPILE_DIR;
		$this->cache_dir = SMARTY_CACHE_DIR;
		$this->setTemplate( $template );
		
		if( $cacheTime > 0 )
			$this->setUpCache( $cacheTime, $cacheId );
	}

	/**
	 * Destructor.
	 */
	public function __destruct() {
		$this->removeCacheLock();
	}
	/**
	* Replacement for Smarty trigger error
	*/
	public function trigger_error($error_msg) {
		throw new RuntimeException( $error_msg );
    }
	
	/**
	* See Smarty::fetch()
	* @return String Fetched template
	*/
	public function fetch() {
		
		try {
		
		if( $this->template === false )
			return '';
		
			
		$templateFileLocation = $this->getTemplateFileLocation();
		
		global $SITE;
		
		// managing partitioners
		if( empty($this->partitioners) === false ) {
		
			$cachedTplPath = DOM_CACHE_PATH . 'DomSite/';
			$uniqueKey = '';
			foreach( $this->partitioners as $dat) 
				$uniqueKey .= $dat[0].'-'.$dat[1];
			
			$cachedTplFile = md5( $SITE->controllersGroup . $this->template . '_' . $uniqueKey ).'.html';
			$create = !file_exists( $cachedTplPath . $cachedTplFile );
			
			// check if any files are modified
			if( DOM_PARTITIONERS_MOD_CHECK === true && $create === false ) {
				
				// check if source template file exists
				if( !is_file($this->template_dir . $this->getTemplateFileLocation()) )
					throw new RuntimeException('Partitioner file <strong>'.$this->template_dir . $this->getTemplateFileLocation().'</strong> does not exist!');
				
				// check if main template is changed
				if( filemtime($this->template_dir . $this->getTemplateFileLocation() ) > filemtime($cachedTplPath . $cachedTplFile) ) {
					$create = true;
					
				// check if any of the partitioners is changed
				} else {
					foreach( $this->partitioners as $template) {
						if( filemtime($this->template_dir . $this->getTemplateFileLocation( $template[1] )) > filemtime($cachedTplPath . $cachedTplFile) ) {
							$create = true;
							break;
						}
					}
				}
			}
			// --------------------------------------
			
			if( $create === true ) {
				
				if( is_dir($cachedTplPath) == false )
					mkdir($cachedTplPath);
				
				$file = file_get_contents( $this->template_dir . $this->getTemplateFileLocation() );
				foreach( $this->partitioners as $template) {
					
					$partitionerFile = $this->template_dir . $this->getTemplateFileLocation( $template[1] );
					if( !is_file($partitionerFile) )
						throw new RuntimeException('Partitioner file <strong>'.$partitionerFile.'</strong> does not exist!');
					
					$partitionerFile = file_get_contents( $partitionerFile );
					$file = str_replace( '{$'.$template[0].'}', $partitionerFile, $file );
				}
				
				file_put_contents( $cachedTplPath . $cachedTplFile, $file );	
			}
			$this->template_dir = $cachedTplPath;
			$templateFileLocation = $cachedTplFile;
		}
		// --------------------------------
		
		$this->assign('pageUrl', $SITE->getUrl() );
		$this->assign('siteUrl', $SITE->getUrl() );
		$this->assign('currentUrl', $SITE->getCurrentUrl() );
		$this->assign('rootUrl', SITE_URL );
		$this->assign('controllerUrl', $SITE->getUrl() . $SITE->selectedController->name.'/' );
		$this->assign('templateUrl',$SITE->getTemplateUrl() );
		$this->assign('mediaUrl',$SITE->getMediaUrl() );
		$this->assign('uploadUrl',DOMAR_FILE_DEFAULT_URL );
		if( class_exists('DomLanguage',false ) )
			$this->assign('languageKey',DomLanguage::getk() );
		
		
		DOM_MONITOR && $t = DomMonitorEntry::c( __CLASS__, ($this->isCached() ? '<b style="color:green">Loaded cache</b> ' : '<b style="color:red">Fetch template</b> ').$this->template );
			
		$result =  parent::fetch( $templateFileLocation, $this->cacheId );
		
		DOM_MONITOR && $t->end();
		
		$this->removeCacheLock();
		
		return $result;
		
		} catch( Exception $e ) {
			$this->removeCacheLock();
			throw $e;
		}
	}
	
	/**
	* Echoes fetched result.
	* Display template
	*/
	public function display() {
		echo $this->fetch();
	}	
	
	// ---------------------------------------------------------------------
	// CACHING
	// ---------------------------------------------------------------------
	/**
	* @param Int $time Cache time in seconds
	* @param String $id Cache id
	* @param Int $cacheType See Smarty::$caching
	*/
	public function setUpCache( $time, $id = NULL, $cacheType = 2 ) {
		if( DOM_VIEW_CACHING === false )
			return;
		$this->caching = (int)$cacheType; 
		$this->cache_lifetime = (int)$time; 
		$this->cacheId = $this->getFullCacheId( $id );
		
		if( !$this->isCached() ) {
			$this->checkCacheLock();
		}
	}
	
	/**
	 * Removes cache lock
	 */
	private function removeCacheLock() {
		$locker = new DomLocker( $this->cacheId );
		$locker->unlock();
	}
	
	/**
	 * Creates cache lock file for cacheId.
	 * Useful for scropts that might take for example 1 second. Then if another user open same page within that time Smarty
	 * would start another cache creating process. This locking disables that, it waits until lock is realeased and then uses
	 * cached file or dies after N seconds of waiting.
	 * Also notice that if script is taking more than N seconds it still allows multiple runs! This is needed to avoid situation
	 * when script created lock file and then died without releasing the lock.
	 */
	private function checkCacheLock() {
		global $SITE;
		
		$locker = new DomLocker( $this->cacheId );
		$locker->setWaitTime( DOM_VIEW_MAX_LOCK_WAIT_TIME );
		$locker->setResetTime( DOM_VIEW_MAX_LOCK_EXIST_TIME );
		
		if( $locker->isLocked() ) {
			$locker->setInfo( 'Lock waited in '.$SITE->getCurrentUrl().' by ip ('.ip().')' );
			$locker->wait();	
		} else {
			$locker->setInfo( 'Locked in '.$SITE->getCurrentUrl().' by ip ('.ip().')' );
			$locker->lock();
		}
	}
	
	public function getFullCacheId( $id ) {
		$prefix = '';
		if( $GLOBALS['SITE'] )
			$prefix = str_replace('/','-',$GLOBALS['SITE']->controllersGroup).'|';
		
		return $prefix . $id;
	}
	/**
	* See Smarty is_cached()
	* @param String $id Cache id If not added object cacheId will be used
	*/
	public function isCached( $id = NULL) {
		return $this->is_cached( $this->getTemplateFileLocation(), $id === NULL ? $this->cacheId : $id );
	}
	
	/**
	* Cleares cache
	* See smarty clear_cache()
	* @param String $id Cache id If not added object cacheId will be used
	*/	
	public function clearCache( $id = NULL ) {
		$this->clear_cache( $this->getTemplateFileLocation(), $id === NULL ? $this->cacheId : $id );
	}
	
	/**
	 * Cleares cache for template and defined cache id.
	 * Use this if you don't have view object for template you want to clear cache for.
	 * @param String $template
	 * @param String $id
	 */
	public static function clearCacheSt( $template, $id ) {
		$view = new DomView( $template );
		$view->caching = 2; 
		$view->cache_lifetime = 1; 
		$view->cacheId = $id;
		$view->clearCache();
	}
	/**
	* Clear all cache. See smarty clear_all_cache()
	*/
	public function clearAllCache() {
		$this->clear_all_cache();
	}
	
	/**
	* @param String $tepmlate
	* Clear compiled templates. See smarty clear_compiled_tpl()
	*/
	public function clearCompiled( $template = NULL ) {
		$this->clear_compiled_tpl( $this->getTemplateFileLocation( $template ) );
	}
	
	/**
	* Clear all compiled templates. See smarty clear_compiled_tpl()
	*/
	public function clearAllCompiled() {
		$this->clear_compiled_tpl();
	}
	
	
	// ---------------------------------------------------------------------
	// PARTITIONERS
	// ---------------------------------------------------------------------	
		
	/**
	* @param String $location
	* @param String $partitioner
	*/
	final public function addPartitioner( $location, $partitioner ) {
		$this->partitioners[] = array($location,$partitioner);
	}
	
	final public function removePartitioner( $location, $partitioner = NULL ) {
		foreach( $this->partitioners as $t => $value ) {
			if( $value[0] == $location && ($partitioner  == NULL or $value[1] == $partitioner)) {
				unset($this->partitioners[$t]);
			}
		}
	} 
	// ---------------------------------------------------------------------
	// SET / GET
	// ---------------------------------------------------------------------	

	/**
	* @param String $template If not given then property $template is used.
	* @return String Template file location relative to smarty templates dir 
	*/
	public function getTemplateFileLocation( $template = NULL ) {
		if( $template === NULL ) $template = $this->template;
		if( $template == NULL ) return NULL;
		
		if( substr($template,0,1) == '/' )
			return substr($template,1);
		else
			return ($GLOBALS['SITE']->controllersGroup ? $GLOBALS['SITE']->controllersGroup.'/' : NULL ).$template;
		
	}

	/**
	* Set smarty template to be fetched
	* @param String $template
	*/
	public function setTemplate( $template ) {
		$this->template = $template;
	}
	
	public function __set( $var, $value ) {
		$this->assign($var, $value);
	}

}
?>